/*  This file is part of CASpp.

    CASpp is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later versio

    CASpp is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CASpp.  If not, see <http://www.gnu.org/licenses/>.

    Copyright 2014 Jérome LAURENS

    See https://code.google.com/p/idalgo/

*/

#include "compare.h"
#include "node.h"

CASPP_NS_BEGIN

Comparison::Comparison(const Values v)
        : value_m(v){
}

bool Comparison::isSame() const {
    return value_m == kSame;
}
bool Comparison::isDifferent() const {
    return value_m == kDifferent;
}
bool Comparison::isAscending() const {
    return value_m == kAscending;
}
bool Comparison::isDescending() const {
    return value_m == kDescending;
}
bool Comparison::isUnknown() const {
    return value_m == kUnknown;
}
bool Comparison::isUndefined() const {
    return value_m == kUndefined;
}
bool Comparison::isDescendingOrEqual() const {
    return isDescending() || isSame();
}
bool Comparison::isAscendingOrEqual() const {
    return isAscending() || isSame();
}

bool Comparison::operator ==(const Comparison & rhs) const {
    return value_m == rhs.value_m;
}
bool Comparison::operator !=(const Comparison & rhs) const {
    return value_m != rhs.value_m;
}

Comparison Comparison::symmetrize() const {
    switch(value_m) {
    case kUndefined: return kUndefined;
    case kAscending: return kDescending;
    case kSame: return kSame;
    case kDifferent: return kDifferent;
    case kDescending: return kAscending;
    case kUnknown: return kUnknown;
    }
}

const std::string Comparison::description() const {
    switch(value_m) {
    case kUndefined: return "Undefined";
    case kAscending: return "<";
    case kSame: return "=";
    case kDifferent: return "≠";
    case kDescending: return ">";
    case kUnknown: return "Unknown";
    }
}

Comparison::Values Comparison::value() const {
    return value_m;
}

class _NodeCompareVisitor: public boost::static_visitor<Comparison> {
public:
    template <typename TRefLeft, typename TRefRight>
    result_type operator() (const TRefLeft & lhs, const TRefRight & rhs) const {
        return compare(*lhs, *rhs);
    }
};

Comparison compare(const Node & lhs, const Node & rhs) {
    return boost::apply_visitor(_NodeCompareVisitor(),lhs,rhs);
}

#ifdef CASPP_DO
#   error This macro should not be defined
#endif
#define CASPP_DO(__S,__T) \
const Comparison compare(const __S & lhs,  const __T & rhs)

#ifdef CASPP_DO_RETURN
#   error This macro should not be defined
#endif
#define CASPP_DO_RETURN(__S,__T,__R) \
CASPP_DO(__S,__T) {return Comparison::__R;}

#ifdef CASPP_DO_SYMMETRIZE
#   error This macro should not be defined
#endif
#define CASPP_DO_SYMMETRIZE(__S,__T) \
CASPP_DO(__S,__T) \
{return compare(rhs,lhs).symmetrize();}

CASPP_DO(Boolean,Boolean) {
    return lhs.value()==rhs.value()? Comparison::kSame: Comparison::kDifferent;
}
CASPP_DO_RETURN(Boolean,String,kUndefined)
CASPP_DO_RETURN(Boolean,List,kUndefined)
CASPP_DO_RETURN(Boolean,Vector,kUndefined)
CASPP_DO_RETURN(Boolean,Set,kUndefined)
CASPP_DO_RETURN(Boolean,Map,kUndefined)
CASPP_DO_RETURN(Boolean,Integer,kUndefined)
CASPP_DO_RETURN(Boolean,Rational,kUndefined)
CASPP_DO_RETURN(Boolean,Real,kUndefined)
CASPP_DO_RETURN(Boolean,Symbol,kUnknown)
CASPP_DO_RETURN(Boolean,Variable,kUnknown)
CASPP_DO_RETURN(Boolean,Function,kUnknown)

CASPP_DO_SYMMETRIZE(String,Boolean)
CASPP_DO(String,String) {
    int what = lhs.compare(rhs);
    if (what<0) return Comparison::kAscending;
    if (what>0) return Comparison::kDescending;
    return Comparison::kSame;
}
CASPP_DO_RETURN(String,List,kUndefined)
CASPP_DO_RETURN(String,Vector,kUndefined)
CASPP_DO_RETURN(String,Set,kUndefined)
CASPP_DO_RETURN(String,Map,kUndefined)
CASPP_DO_RETURN(String,Integer,kUndefined)
CASPP_DO_RETURN(String,Rational,kUndefined)
CASPP_DO_RETURN(String,Real,kUndefined)
CASPP_DO_RETURN(String,Symbol,kUnknown)
CASPP_DO_RETURN(String,Variable,kUnknown)
CASPP_DO_RETURN(String,Function,kUnknown)

CASPP_DO_SYMMETRIZE(List,Boolean)
CASPP_DO_SYMMETRIZE(List,String)
CASPP_DO(List,List) {
    List::const_iterator lhsItor = lhs.begin(), lhsEnd = lhs.end();
    List::const_iterator rhsItor = rhs.begin(), rhsEnd = rhs.end();
    do {
        if (lhsItor != lhsEnd) {
            if (rhsItor != rhsEnd) {
                Comparison c = compare<List::value_type>(*lhsItor,*rhsItor);
                if (!c.isSame()) {
                    return c;
                }
            } else {
                return Comparison::kDescending;
            }
        } else if (rhsItor != rhsEnd) {
            return Comparison::kAscending;
        } else {
            return Comparison::kSame;
        }
        ++lhsItor;
        ++rhsItor;
    } while(true);
}
CASPP_DO_RETURN(List,Vector,kUndefined)
CASPP_DO_RETURN(List,Set,kUndefined)
CASPP_DO_RETURN(List,Map,kUndefined)
CASPP_DO_RETURN(List,Integer,kUndefined)
CASPP_DO_RETURN(List,Rational,kUndefined)
CASPP_DO_RETURN(List,Real,kUndefined)
CASPP_DO_RETURN(List,Symbol,kUnknown)
CASPP_DO_RETURN(List,Variable,kUnknown)
CASPP_DO_RETURN(List,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Vector,Boolean)
CASPP_DO_SYMMETRIZE(Vector,String)
CASPP_DO_SYMMETRIZE(Vector,List)
CASPP_DO(Vector,Vector) {
    Vector::const_iterator lhsItor = lhs.begin(), lhsEnd = lhs.end();
    Vector::const_iterator rhsItor = rhs.begin(), rhsEnd = rhs.end();
    do {
        if (lhsItor != lhsEnd) {
            if (rhsItor != rhsEnd) {
                Comparison c = compare<Vector::value_type>(*lhsItor,*rhsItor);
                if (!c.isSame()) {
                    return c;
                }
            } else {
                return Comparison::kDescending;
            }
        } else if (rhsItor != rhsEnd) {
            return Comparison::kAscending;
        } else {
            return Comparison::kSame;
        }
        ++lhsItor;
        ++rhsItor;
    } while(true);
}
CASPP_DO_RETURN(Vector,Set,kUndefined)
CASPP_DO_RETURN(Vector,Map,kUndefined)
CASPP_DO_RETURN(Vector,Integer,kUndefined)
CASPP_DO_RETURN(Vector,Rational,kUndefined)
CASPP_DO_RETURN(Vector,Real,kUndefined)
CASPP_DO_RETURN(Vector,Symbol,kUnknown)
CASPP_DO_RETURN(Vector,Variable,kUnknown)
CASPP_DO_RETURN(Vector,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Set,Boolean)
CASPP_DO_SYMMETRIZE(Set,String)
CASPP_DO_SYMMETRIZE(Set,List)
CASPP_DO_SYMMETRIZE(Set,Vector)
CASPP_DO(Set,Set) {
#warning NYI set comparison
    // order the set then use List comparison
    Set::const_iterator lhsItor = lhs.begin(), lhsEnd = lhs.end();
    Set::const_iterator rhsItor = rhs.begin(), rhsEnd = rhs.end();
    do {
        if (lhsItor != lhsEnd) {
            if (rhsItor != rhsEnd) {
                Comparison c = compare<Set::value_type>(*lhsItor,*rhsItor);
                if (!c.isSame()) {
                    return c;
                }
            } else {
                return Comparison::kDescending;
            }
        } else if (rhsItor != rhsEnd) {
            return Comparison::kAscending;
        } else {
            return Comparison::kSame;
        }
        ++lhsItor;
        ++rhsItor;
    } while(true);
}
CASPP_DO_RETURN(Set,Map,kUndefined)
CASPP_DO_RETURN(Set,Integer,kUndefined)
CASPP_DO_RETURN(Set,Rational,kUndefined)
CASPP_DO_RETURN(Set,Real,kUndefined)
CASPP_DO_RETURN(Set,Symbol,kUnknown)
CASPP_DO_RETURN(Set,Variable,kUnknown)
CASPP_DO_RETURN(Set,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Map,Boolean)
CASPP_DO_SYMMETRIZE(Map,String)
CASPP_DO_SYMMETRIZE(Map,List)
CASPP_DO_SYMMETRIZE(Map,Vector)
CASPP_DO_SYMMETRIZE(Map,Set)
CASPP_DO(Map,Map) {
    Map::const_iterator lhsItor = lhs.begin(), lhsEnd = lhs.end();
    Map::const_iterator rhsItor = rhs.begin(), rhsEnd = rhs.end();
    do {
        if (lhsItor!=lhsEnd) {
            if (rhsItor!=rhsEnd) {
                Comparison c = compare(*lhsItor->first,*rhsItor->first);
                if(c.isSame()) {
                    c = compare(*lhsItor->second,*rhsItor->second);
                    if(c.isSame()) {
                        ++lhsItor;
                        ++rhsItor;
                    } else {
                        return c;
                    }
                } else {
                    return c;
                }
            } else {
                return Comparison::kDescending;
            }
        } else if (rhsItor!=rhsEnd) {
            return Comparison::kAscending;
        } else {
            return Comparison::kSame;
        }
    } while(true);
}
CASPP_DO_RETURN(Map,Integer,kUndefined)
CASPP_DO_RETURN(Map,Rational,kUndefined)
CASPP_DO_RETURN(Map,Real,kUndefined)
CASPP_DO_RETURN(Map,Symbol,kUnknown)
CASPP_DO_RETURN(Map,Variable,kUnknown)
CASPP_DO_RETURN(Map,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Integer,Boolean)
CASPP_DO_SYMMETRIZE(Integer,String)
CASPP_DO_SYMMETRIZE(Integer,List)
CASPP_DO_SYMMETRIZE(Integer,Vector)
CASPP_DO_SYMMETRIZE(Integer,Set)
CASPP_DO_SYMMETRIZE(Integer,Map)
CASPP_DO(Integer,Integer) {
    const mpz_t& L = lhs.backend().data();
    const mpz_t& R = rhs.backend().data();
    int c = mpz_cmp(L,R);
    return c<0? Comparison::kAscending: (c>0? Comparison::kDescending: Comparison::kSame);
}
CASPP_DO(Integer,Rational) {
    Rational q = makeRational(lhs);
    return compare(q,rhs);
}
CASPP_DO(Integer,Real) {
    Real x = makeReal(lhs);
    return compare(x,rhs);
}
CASPP_DO_RETURN(Integer,Symbol,kUnknown)
CASPP_DO_RETURN(Integer,Variable,kUnknown)
CASPP_DO_RETURN(Integer,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Rational,Boolean)
CASPP_DO_SYMMETRIZE(Rational,String)
CASPP_DO_SYMMETRIZE(Rational,List)
CASPP_DO_SYMMETRIZE(Rational,Vector)
CASPP_DO_SYMMETRIZE(Rational,Set)
CASPP_DO_SYMMETRIZE(Rational,Map)
CASPP_DO_SYMMETRIZE(Rational,Integer)
CASPP_DO(Rational,Rational) {
    const mpq_t& L = lhs.backend().data();
    const mpq_t& R = rhs.backend().data();
    int c = mpq_cmp(L,R);
    return c<0? Comparison::kAscending: (c>0? Comparison::kDescending: Comparison::kSame);
}
CASPP_DO(Rational,Real) {
    Real x = makeReal(lhs);
    return compare(x,rhs);
}
CASPP_DO_RETURN(Rational,Symbol,kUnknown)
CASPP_DO_RETURN(Rational,Variable,kUnknown)
CASPP_DO_RETURN(Rational,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Real,Boolean)
CASPP_DO_SYMMETRIZE(Real,String)
CASPP_DO_SYMMETRIZE(Real,List)
CASPP_DO_SYMMETRIZE(Real,Vector)
CASPP_DO_SYMMETRIZE(Real,Set)
CASPP_DO_SYMMETRIZE(Real,Map)
CASPP_DO_SYMMETRIZE(Real,Integer)
CASPP_DO_SYMMETRIZE(Real,Rational)
CASPP_DO(Real,Real) {
    const mpfr_t& L = lhs.backend().data();
    const mpfr_t& R = rhs.backend().data();
    int c = mpfr_cmp(L,R);
    return c<0? Comparison::kAscending: (c>0? Comparison::kDescending: Comparison::kSame);
}
CASPP_DO_RETURN(Real,Symbol,kUnknown)
CASPP_DO_RETURN(Real,Variable,kUnknown)
CASPP_DO_RETURN(Real,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Symbol,Boolean)
CASPP_DO_SYMMETRIZE(Symbol,String)
CASPP_DO_SYMMETRIZE(Symbol,List)
CASPP_DO_SYMMETRIZE(Symbol,Vector)
CASPP_DO_SYMMETRIZE(Symbol,Set)
CASPP_DO_SYMMETRIZE(Symbol,Map)
CASPP_DO_SYMMETRIZE(Symbol,Integer)
CASPP_DO_SYMMETRIZE(Symbol,Rational)
CASPP_DO_SYMMETRIZE(Symbol,Real)
CASPP_DO(Symbol,Symbol) {
    int what = lhs.name().compare(rhs.name());
    if (what<0) return Comparison::kAscending;
    if (what>0) return Comparison::kDescending;
    return Comparison::kSame;
}
CASPP_DO_RETURN(Symbol,Variable,kUnknown)
CASPP_DO_RETURN(Symbol,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Variable,Boolean)
CASPP_DO_SYMMETRIZE(Variable,String)
CASPP_DO_SYMMETRIZE(Variable,List)
CASPP_DO_SYMMETRIZE(Variable,Vector)
CASPP_DO_SYMMETRIZE(Variable,Set)
CASPP_DO_SYMMETRIZE(Variable,Map)
CASPP_DO_SYMMETRIZE(Variable,Integer)
CASPP_DO_SYMMETRIZE(Variable,Rational)
CASPP_DO_SYMMETRIZE(Variable,Real)
CASPP_DO_SYMMETRIZE(Variable,Symbol)
CASPP_DO(Variable,Variable) {
    int what = lhs.name().compare(rhs.name());
    if (what<0) return Comparison::kAscending;
    if (what>0) return Comparison::kDescending;
    return Comparison::kSame;
}
CASPP_DO_RETURN(Variable,Function,kUnknown)

CASPP_DO_SYMMETRIZE(Function,Boolean)
CASPP_DO_SYMMETRIZE(Function,String)
CASPP_DO_SYMMETRIZE(Function,List)
CASPP_DO_SYMMETRIZE(Function,Vector)
CASPP_DO_SYMMETRIZE(Function,Set)
CASPP_DO_SYMMETRIZE(Function,Map)
CASPP_DO_SYMMETRIZE(Function,Integer)
CASPP_DO_SYMMETRIZE(Function,Rational)
CASPP_DO_SYMMETRIZE(Function,Real)
CASPP_DO_SYMMETRIZE(Function,Symbol)
CASPP_DO_SYMMETRIZE(Function,Variable)
CASPP_DO(Function,Function) {
    Comparison c = compare(lhs.symbolRef(),rhs.symbolRef());
    if (c.isSame()) {
        return compare(lhs.argumentsRef(),rhs.argumentsRef());
    } else {
        return c;
    }
}

#undef CASPP_DO_SYMMETRIZE
#undef CASPP_DO_RETURN
#undef CASPP_DO

#ifdef CASPP___DO
#   error This macro should not be defined
#endif
#define CASPP___DO(lhs,rhs) compare(lhs,rhs)

#define CASPP__DO(__S,__T)\
    const Boolean operator < (const __S & lhs,  const __T & rhs) { \
        Comparison c = CASPP___DO(lhs,rhs); \
        if(c.isUndefined())  return Boolean::kUndefined; \
        if(c.isAscending())  return Boolean::kTrue; \
        if(c.isSame())       return Boolean::kFalse; \
        if(c.isDifferent())  return Boolean::kUnknown; \
        if(c.isDescending()) return Boolean::kFalse; \
        return Boolean::kUnknown; \
    } \
    const Boolean operator <= (const __S & lhs,  const __T & rhs) { \
        Comparison c = CASPP___DO(lhs,rhs); \
        if(c.isUndefined())  return Boolean::kUndefined; \
        if(c.isAscending())  return Boolean::kTrue; \
        if(c.isSame())       return Boolean::kTrue; \
        if(c.isDifferent())  return Boolean::kUnknown; \
        if(c.isDescending()) return Boolean::kFalse; \
        return Boolean::kUnknown; \
    } \
    const Boolean operator > (const __S & lhs,  const __T & rhs) { \
        Comparison c = CASPP___DO(lhs,rhs); \
        if(c.isUndefined())  return Boolean::kUndefined; \
        if(c.isAscending())  return Boolean::kFalse; \
        if(c.isSame())       return Boolean::kFalse; \
        if(c.isDifferent())  return Boolean::kUnknown; \
        if(c.isDescending()) return Boolean::kTrue; \
        return Boolean::kUnknown; \
    } \
    const Boolean operator >= (const __S & lhs,  const __T & rhs) { \
        Comparison c = CASPP___DO(lhs,rhs); \
        if(c.isUndefined())  return Boolean::kUndefined; \
        if(c.isAscending())  return Boolean::kFalse; \
        if(c.isSame())       return Boolean::kTrue; \
        if(c.isDifferent())  return Boolean::kUnknown; \
        if(c.isDescending()) return Boolean::kTrue; \
        return Boolean::kUnknown; \
    } \
    const Boolean operator == (const __S & lhs,  const __T & rhs) { \
        Comparison c = CASPP___DO(lhs,rhs); \
        if(c.isUndefined())  return Boolean::kUndefined; \
        if(c.isAscending())  return Boolean::kFalse; \
        if(c.isSame())       return Boolean::kTrue; \
        if(c.isDifferent())  return Boolean::kFalse; \
        if(c.isDescending()) return Boolean::kFalse; \
        return Boolean::kUnknown; \
    } \
    const Boolean operator != (const __S & lhs,  const __T & rhs) { \
        return ! (lhs==rhs); \
    }

#define CASPP_DO(__L)\
    CASPP__DO(__L,Boolean) \
    CASPP__DO(__L,String) \
    CASPP__DO(__L,Vector) \
    CASPP__DO(__L,Map) \
    CASPP__DO(__L,Integer) \
    CASPP__DO(__L,Rational) \
    CASPP__DO(__L,Real) \
    CASPP__DO(__L,Symbol) \
    CASPP__DO(__L,Variable) \
    CASPP__DO(__L,Function)

CASPP_DO(Boolean)
CASPP_DO(String)
CASPP_DO(Vector)
CASPP_DO(Map)
CASPP_DO(Integer)
CASPP_DO(Rational)
CASPP_DO(Real)
CASPP_DO(Symbol)
CASPP_DO(Variable)
CASPP_DO(Function)

#undef CASPP_DO

CASPP__DO(Node,Node);

#undef CASPP___DO
#define CASPP___DO(lhs,rhs) compare(*lhs,*rhs)

CASPP__DO(NodeRef,NodeRef);

#undef CASPP__DO
#undef CASPP___DO

CASPP_NS_END

